`include "defines.svh"
`include "axi_defines.svh"
`default_nettype none

// parameter SRAM_BASE_ADDR  = 32'h8000_0000, // SRAM基地址
// parameter SRAM_ADDR_MASK  = 32'hF000_0000,
// parameter CLINT_BASE_ADDR = 32'hA000_0048, // CLINT基地址
// parameter CLINT_ADDR_MASK = 32'hFFFF_FFF8,
// parameter DEV_BASE_ADDR   = 32'hA000_0000, // DEV基地址(接到ram上)
// parameter DEV_ADDR_MASK   = 32'hF000_0000

module axi_xbar #(
    parameter AMDEV_BASE_ADDR = 32'hA000_0000, // AMDEV基地址
    parameter AMDEV_ADDR_MASK = 32'hA800_0000,

    parameter MROM_BASE_ADDR  = 32'h2000_0000, // MROM基地址
    parameter MROM_ADDR_MASK  = 32'hFFFF_F000,
    parameter CLINT_BASE_ADDR = 32'h0200_0000, // CLINT基地址
    parameter CLINT_ADDR_MASK = 32'hFFFF_F000,
    parameter SRAM_BASE_ADDR  = 32'h0F00_0000, // SRAM基地址
    parameter SRAM_ADDR_MASK  = 32'hFFFF_E000,
    parameter UART_BASE_ADDR  = 32'h1000_0000, // UART基地址
    parameter UART_ADDR_MASK  = 32'hFFFF_F000,
    parameter SPI_BASE_ADDR   = 32'h1000_1000, // SPI基地址
    parameter SPI_ADDR_MASK   = 32'hFFFF_F000,
    parameter FLASH_BASE_ADDR   = 32'h3000_0000, // FLASH基地址
    parameter FLASH_ADDR_MASK   = 32'hF000_0000,
    parameter PSRAM_BASE_ADDR   = 32'h8000_0000, // PSRAM基地址
    parameter PSRAM_ADDR_MASK   = 32'hE000_0000,
    parameter SDRAM_BASE_ADDR   = 32'hA000_0000, // SDRAM基地址
    parameter SDRAM_ADDR_MASK   = 32'hA000_0000
)(
    input clk,
    // Master Interface
    input  ysyx_data_t m_debug_pc,
    input  logic  m_awvalid,
    output logic  m_awready,
    input  ysyx_addr_t m_awaddr,

    input  logic  m_wvalid,
    output logic  m_wready,
    input  ysyx_data_t m_wdata,
    input  ysyx_strb_t m_wstrb,

    output logic  m_bvalid,
    input  logic  m_bready,
    output ysyx_resp_t m_bresp,

    input  logic  m_arvalid,
    output logic  m_arready,
    input  ysyx_addr_t m_araddr,

    output logic  m_rvalid,
    input  logic  m_rready,
    output ysyx_data_t m_rdata,
    output ysyx_resp_t m_rresp,

    // SRAM Slave Interface
    // (sram实际指连接soc的通路，不是sram本身)
    output ysyx_data_t sram_debug_pc,
    output logic  sram_awvalid,
    input  logic  sram_awready,
    output ysyx_addr_t sram_awaddr,

    output logic  sram_wvalid,
    input  logic  sram_wready,
    output ysyx_data_t sram_wdata,
    output ysyx_strb_t sram_wstrb,

    input  logic  sram_bvalid,
    output logic  sram_bready,
    input  ysyx_resp_t sram_bresp,

    output logic  sram_arvalid,
    input  logic  sram_arready,
    output ysyx_addr_t sram_araddr,

    input  logic  sram_rvalid,
    output logic  sram_rready,
    input  ysyx_data_t sram_rdata,
    input  ysyx_resp_t sram_rresp,

    // CLINT Slave Interface
    output logic  clint_awvalid,
    input  logic  clint_awready,
    output ysyx_addr_t clint_awaddr,

    output logic  clint_wvalid,
    input  logic  clint_wready,
    output ysyx_data_t clint_wdata,
    output ysyx_strb_t clint_wstrb,

    input  logic  clint_bvalid,
    output logic  clint_bready,
    input  ysyx_resp_t clint_bresp,

    output logic  clint_arvalid,
    input  logic  clint_arready,
    output ysyx_addr_t clint_araddr,

    input  logic  clint_rvalid,
    output logic  clint_rready,
    input  ysyx_data_t clint_rdata,
    input  ysyx_resp_t clint_rresp,

    // AMDEV Slave Interface
    output logic  amdev_awvalid,
    input  logic  amdev_awready,
    output ysyx_addr_t amdev_awaddr,

    output logic  amdev_wvalid,
    input  logic  amdev_wready,
    output ysyx_data_t amdev_wdata,
    output ysyx_strb_t amdev_wstrb,

    input  logic  amdev_bvalid,
    output logic  amdev_bready,
    input  ysyx_resp_t amdev_bresp,

    output logic  amdev_arvalid,
    input  logic  amdev_arready,
    output ysyx_addr_t amdev_araddr,

    input  logic  amdev_rvalid,
    output logic  amdev_rready,
    input  ysyx_data_t amdev_rdata,
    input  ysyx_resp_t amdev_rresp,

    // extra signals
    input axi_slave_read_in_t m_extra_rin,
    output axi_slave_read_out_t m_extra_rout,
    input axi_slave_write_in_t m_extra_win,
    output axi_slave_write_out_t m_extra_wout,

    input axi_master_read_in_t sram_extra_rin,
    output axi_master_read_out_t sram_extra_rout,
    input axi_master_write_in_t sram_extra_win,
    output axi_master_write_out_t sram_extra_wout
);

    // Address decoding

    // 内部设备
    wire clint_selected = ((m_araddr & CLINT_ADDR_MASK) == CLINT_BASE_ADDR);

    // dpic设备（仅am仿真用）
    wire amdev_selected = ((m_awaddr & AMDEV_ADDR_MASK ) == AMDEV_BASE_ADDR ) ||
                          ((m_araddr & AMDEV_ADDR_MASK ) == AMDEV_BASE_ADDR ); //RW

    // 外部设备
    wire mrom_selected  = ((m_araddr & MROM_ADDR_MASK ) == MROM_BASE_ADDR ); //RD
    
    wire sram_selected  = ((m_awaddr & SRAM_ADDR_MASK ) == SRAM_BASE_ADDR ) ||
                          ((m_araddr & SRAM_ADDR_MASK ) == SRAM_BASE_ADDR ); //RW
    wire uart_selected  = ((m_araddr & UART_ADDR_MASK ) == UART_BASE_ADDR ) ||
                          ((m_awaddr & UART_ADDR_MASK ) == UART_BASE_ADDR ); //RW
    wire spi_selected   = ((m_araddr & SPI_ADDR_MASK  ) == SPI_BASE_ADDR  ) || 
                          ((m_awaddr & SPI_ADDR_MASK  ) == SPI_BASE_ADDR  ); //RW
    wire flash_selected = ((m_araddr & FLASH_ADDR_MASK) == FLASH_BASE_ADDR); //RD
    wire psram_selected = ((m_araddr & PSRAM_ADDR_MASK) == PSRAM_BASE_ADDR) ||
                          ((m_awaddr & PSRAM_ADDR_MASK) == PSRAM_BASE_ADDR); //RW
    wire sdram_selected = ((m_araddr & SDRAM_ADDR_MASK) == SDRAM_BASE_ADDR) ||
                          ((m_awaddr & SDRAM_ADDR_MASK) == SDRAM_BASE_ADDR); //RW
    wire outside_selected = mrom_selected || sram_selected || uart_selected || spi_selected || flash_selected || psram_selected || sdram_selected;

    assign sram_debug_pc = m_debug_pc;


    wire mrom_error  = ((m_awaddr & MROM_ADDR_MASK ) == MROM_BASE_ADDR );
    wire flash_error = ((m_awaddr & FLASH_ADDR_MASK) == FLASH_BASE_ADDR);

    // xbar报错
    always_ff @(posedge clk) begin
        if(mrom_error || flash_error) $display("[XBar Error]:not support operation (WRITE) in (%x/%x) at pc = (%x)",m_araddr,m_awaddr,m_debug_pc);
        else if(~(outside_selected || amdev_selected) && m_debug_pc != 32'h0) $display("[XBar Error]:not support addr (%x/%x) at pc = (%x)",m_araddr,m_awaddr,m_debug_pc);
    end

    // Write Address Channel
    assign sram_awvalid  = m_awvalid && outside_selected;
    assign clint_awvalid = m_awvalid && clint_selected;
    assign amdev_awvalid = m_awvalid && amdev_selected;
    assign sram_awaddr   = m_awaddr;
    assign clint_awaddr  = m_awaddr;
    assign amdev_awaddr  = m_awaddr;
    assign m_awready     = (outside_selected  ? sram_awready  : `OFF) |
                           (clint_selected    ? clint_awready : `OFF) | 
                           (amdev_selected    ? amdev_awready : `OFF);

    // Write Data Channel
    assign sram_wvalid  = m_wvalid && outside_selected;
    assign clint_wvalid = m_wvalid && clint_selected;
    assign amdev_wvalid = m_wvalid && amdev_selected;
    assign sram_wdata   = m_wdata;
    assign clint_wdata  = m_wdata;
    assign amdev_wdata  = m_wdata;
    assign sram_wstrb   = m_wstrb;
    assign clint_wstrb  = m_wstrb;
    assign amdev_wstrb  = m_wstrb;
    assign m_wready     = (outside_selected  ? sram_wready  : `OFF) |
                          (clint_selected    ? clint_wready : `OFF) |
                          (amdev_selected    ? amdev_wready : `OFF);

    // Write Response Channel
    assign m_bvalid = (outside_selected  ? sram_bvalid  : `OFF) |
                      (clint_selected    ? clint_bvalid : `OFF) |
                      (amdev_selected    ? amdev_bvalid : `OFF);
    assign m_bresp  = (outside_selected  ? sram_bresp   : `NULL) |
                      (clint_selected    ? clint_bresp  : `NULL) |
                      (amdev_selected    ? amdev_bresp  : `NULL);
    assign sram_bready = m_bready && outside_selected;
    assign clint_bready = m_bready && clint_selected;
    assign amdev_bready = m_bready && amdev_selected;

    // Write Extra Channel
    assign m_extra_wout     = outside_selected  ? sram_extra_win  : `NULL;
    assign sram_extra_wout  = outside_selected  ? m_extra_win     : `NULL;





    // Read Address Channel
    assign sram_arvalid = m_arvalid && outside_selected;
    assign clint_arvalid = m_arvalid && clint_selected;
    assign amdev_arvalid = m_arvalid && amdev_selected;
    assign sram_araddr  = m_araddr;
    assign clint_araddr  = m_araddr;
    assign amdev_araddr  = m_araddr;
    assign m_arready    = (outside_selected  ? sram_arready  : `OFF) |
                          (clint_selected    ? clint_arready : `OFF) |
                          (mrom_selected     ? sram_arready  : `OFF) | 
                          (amdev_selected    ? amdev_arready : `OFF);

    // Read Data Channel
    assign m_rvalid = (outside_selected  ? sram_rvalid  : `OFF) |
                      (clint_selected    ? clint_rvalid : `OFF) |
                      (mrom_selected     ? sram_rvalid  : `OFF) |
                      (amdev_selected    ? amdev_rvalid : `OFF);

    assign m_rdata  = (outside_selected  ? sram_rdata   : `NULL) |
                      (clint_selected    ? clint_rdata  : `NULL) |
                      (mrom_selected     ? sram_rdata   : `NULL) |
                      (amdev_selected    ? amdev_rdata  : `NULL);

    assign m_rresp  = (outside_selected  ? sram_rresp   : `NULL) |
                      (clint_selected    ? clint_rresp  : `NULL) |
                      (mrom_selected     ? sram_rresp   : `NULL) |
                      (amdev_selected    ? amdev_rresp  : `NULL);

    assign sram_rready = m_rready && outside_selected;
    assign clint_rready = m_rready && clint_selected;
    assign amdev_rready = m_rready && amdev_selected;

    // Read Extra Channel
    assign m_extra_rout = outside_selected  ? sram_extra_rin  : `NULL;
    assign sram_extra_rout  = outside_selected  ? m_extra_rin  : `NULL;
endmodule
